<?php 
/*******************************
* http����ģ��
* @author Erik
* @version 0.2
* @since 2008-04-11
*******************************/

/**
* http������
*/
class WebRequest {
    var $url        = '';
    var $host        = '';
    var $port        = 80;
    var $path        = '/';
    var $method        = '';
    var $postdata    = '';
    var $cookies    = array();

    var $accept                = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
    var $accept_language    = 'zh-cn';
    var $accept_encoding    = 'gzip';
    var $user_agent            = 'ErikClient/1.0';
    var $use_gzip            = false;

    var $username;
    var $password;

    var $timeout = 20;

    /**
    * ���캯��
    * @public
    * @param <string> $url �����ַ
    * @param <string> $method ����ʽ
    * @param <string> $postData post����ݡ�$methodΪ��postʱ�ò�����Ч
    */
    function WebRequest ($url, $method = 'get', $postData = '') {
        $this->url = $url;
        $this->method = $method;
        $this->postdata = $postData;

        $urlPattern = "/^http:\/\/([^\/]+)(\/.*)?/i";

        if (preg_match($urlPattern, $url, $urlArr)) {
            $hostStr = $urlArr[1];
           
            $hosts = preg_split("/:/i", $hostStr);
            $this->host = $hosts[0];
           
            if (count($hosts) > 1) {
                $this->port = $hosts[1];
            }
            if (count($urlArr) > 2) {
                $this->path = $urlArr[2];
            }
        } else {
        }
    }

    /**
    * ��ȡ�������Ӧ
    * @public
    * @return <WebResponse> http�������Ӧ��Ϣ
    */
    public function getResponse () {
        if (!$fp = fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
            switch($errno) {
                case -3:
                    $this->errormsg = 'Socket连接创建失败 (-3)';
                    break;
                case -4:
                    $this->errormsg = 'DNS定位失败 (-4)';
                    break;
                case -5:
                    $this->errormsg = '连接超时或被拒绝 (-5)';
                    break;
                default:
                    $this->errormsg = '连接失败('.$errno.')';
                    break;
                $this->errormsg .= ' '.$errstr;
            }
            return false;
        }
        socket_set_timeout($fp, $this->timeout);

        $request = $this->buildRequestInfo();
        fwrite($fp, $request);

        $content = '';
        $readState = 'start';
        $response = new WebResponse();
        while (!feof($fp)) {
            $line = fgets($fp, 4096);
            if ($readState == 'start') {
                $readState = 'header';
                if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
                    $this->errormsg = "非法的请求状态:" . htmlentities($line);
                    return false;
                }
                $http_version = $m[1]; //δʹ��
                $response->setStatus($m[2]);
                $status_string = $m[3]; //δʹ��
            } else if ($readState == 'header') {
                if (trim($line) == '') {
                    $readState = 'content';
                }

                if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
                    continue;
                }

                $key = strtolower(trim($m[1]));
                $val = trim($m[2]);
                $response->appendHeader($key, $val);
            } else {
                $content .= $line;
            }
        }
        fclose($fp);
        //$response->setContent($content);

        return $response;
    }

    /**
    * ������socket���͵�������Ϣ
    * @private
    * @return <string> request��Ϣ
    */
    private function buildRequestInfo () {
        $headers = array();
        $method = strtoupper($this->method);
        if ($method != 'POST') $method = 'GET';

        $headers[] = "{$method} {$this->path} HTTP/1.0";
        $headers[] = "Host: {$this->host}";
        $headers[] = "User-Agent: {$this->user_agent}";
        $headers[] = "Accept: {$this->accept}";
        if ($this->use_gzip) {
            $headers[] = "Accept-Encoding: {$this->accept_encoding}";
        }
        $headers[] = "Accept-Language: {$this->accept_language}";

        // Cookies
        if ($this->cookies) {
            $cookie = 'Cookie: ';
            foreach ($this->cookies as $key => $value) {
                $cookie .= "$key=$value; ";
            }
            $headers[] = $cookie;
        }

        // authentication
        if ($this->username && $this->password) {
            $headers[] = 'Authorization: BASIC ' . base64_encode($this->username.':'.$this->password);
        }

        // �����POST��ʽ, ����content type��lengthͷ
        if ($method == 'POST') {
            $headers[] = 'Content-Type: application/x-www-form-urlencoded';
            $headers[] = 'Content-Length: ' . strlen($this->postdata);
        }

        $request = implode("\r\n", $headers) . "\r\n\r\n" . $this->postdata;
        return $request;
    }

    /**
    * ����cookies
    * @public
    * @param <array> $array cookies
    */
    public function setCookies($array) {
        $this->cookies = $array;
    }
}

/**
* http��Ӧ��
*/
class WebResponse {
    var $status;
    var $headers = array();
    var $content;

    function WebResponse () {
    }

    /**
    * ״̬������
    * @public
    * @param <string> $sta ״̬��
    */
    public function setStatus ($sta) {
        $this->status = $sta;
    }

    /**
    * ���httpͷ��Ϣ
    * @public
    * @param <string> $ke httpͷ��
    * @param <string> $value httpͷֵ
    */
    public function appendHeader ($ke, $value) {
        if (isset($this->headers[$ke])) {
            if (is_array($this->headers[$ke])) {
                $this->headers[$ke][] = $value;
            } else {
                $this->headers[$ke] = array($this->headers[$ke], $value);
            }
        } else {
            $this->headers[$ke] = $value;
        }
    }

    /**
    * ����response������
    * @public
    * @param <string> $content response������
    */
    public function setContent ($content) {
        $this->content = $content;
    }

    /**
    * ��ȡresponse������
    * @public
    * @return <string> response������
    */
    public function getContent () {
        return $this->content;
    }

    /**
    * ��ȡresponse headers
    * @public
    * @return <array> response��headers
    */
    public function getHeaders () {
        return $this->headers;
    }
}

?>